package cn.bugstack.mybatis.builder.xml;

import cn.bugstack.mybatis.builder.BaseBuilder;
import cn.bugstack.mybatis.datasource.DataSourceFactory;
import cn.bugstack.mybatis.io.Resources;
import cn.bugstack.mybatis.mapping.*;
import cn.bugstack.mybatis.plugin.Interceptor;
import cn.bugstack.mybatis.session.Configuration;
import cn.bugstack.mybatis.transaction.TransactionFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import javax.sql.DataSource;
import java.io.Reader;
import java.util.*;

public class XmlConfigBuilder extends BaseBuilder {

    private Element root;

    public XmlConfigBuilder(Reader reader) {
        super(new Configuration());
        // dom4j parse xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(new InputSource(reader));
            root = document.getRootElement();
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }

    }

    public Configuration parse() throws Exception {

        pluginsElement(root.element("plugins"));
        environmentElement(root.element("environments"));
        mapperElement(root.element("mappers"));
        return configuration;
    }

    private void pluginsElement(Element pluginsElement) throws Exception {
        if (pluginsElement == null) {
            return;
        }
        List<Element> pluginList = pluginsElement.elements();
        for (Element pluginElement : pluginList) {
            String interceptor = pluginElement.attributeValue("interceptor");
            Properties properties = new Properties();
            List<Element> propertyList = pluginElement.elements("property");
            for (Element propertyElement : propertyList) {
                properties.setProperty(propertyElement.attributeValue("name"), propertyElement.attributeValue("value"));
            }
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
            interceptorInstance.setProperties(properties);
            configuration.addInterceptor(interceptorInstance);
        }
    }

    private void environmentElement(Element environments) throws Exception{
        String defaultEnv = environments.attributeValue("default");
        List<Element> environmentList = environments.elements("environment");
        for (Element environment : environmentList) {
            String id = environment.attributeValue("id");
            if (id.equals(defaultEnv)) {
                Element transactionManager = environment.element("transactionManager");
                String transactionManagerType = transactionManager.attributeValue("type");
                TransactionFactory transactionFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(transactionManagerType).newInstance();


                Element dataSourceElement = environment.element("dataSource");
                String type = dataSourceElement.attributeValue("type");
                DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry.resolveAlias(type).newInstance();
                Properties properties = new Properties();
                for (Element property : dataSourceElement.elements("property")) {
                    String name = property.attributeValue("name");
                    String value = property.attributeValue("value");
                    properties.put(name, value);
                }
                dataSourceFactory.setProperties(properties);
                DataSource dataSource = dataSourceFactory.getDataSource();
                Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(transactionFactory).dataSource(dataSource);
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }

    private void mapperElement(Element mappers) throws Exception {
        List<Element> mapperList = mappers.elements("mapper");
        for (Element mapper : mapperList) {
            String resource = mapper.attributeValue("resource");
            String mapperClass = mapper.attributeValue("class");
            if (resource != null && mapperClass == null) {
                XmlMapperBuilder xmlMapperBuilder = new XmlMapperBuilder(configuration, mapper,mapper.attributeValue("resource"));
                xmlMapperBuilder.parse();
            } else if (resource == null && mapperClass != null) {
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                configuration.addMapper(mapperInterface);
            }

        }
    }

}
